//对象,学习源码,尝试写迷你型demo,然后在阅读实际代码
/**
 * proxy 可以拦截所以的属性操作,还支持全部的数据格式Map,懒收集
 * proxy浏览器自带的功能
 *
 * defineProperty 数组需要单独拦截
 * 对象新增和删除属性，不能拦截，需要单独处理$set,$delete
 */
const baseHandler = {
  get(target, key) {
    //const res = target[key];下面方法更好
    const res = Reflect.get(target, key);
    //依赖收集，如何体现呢，通过尝试获取值obj.age，触发了getter
    track(target, key);
    return typeof res === "object" ? reactive(res) : res;
  },
  set(target, key, val) {
    const info = { oldValue: target[key], newValue: val };
    //target[key] = val;
    const res = Reflect.set(target, key, val);
    //todo 响应式去通知变化,触发执行effect
    trigger(target, key, info);
  },
};

function reactive(target) {
  //vue还需要考虑map对象
  const observed = new Proxy(target, baseHandler);
  //返回响应式对象
  return observed;
}

function computed(fn) {
  //特殊的effect,lazy表示首次不运行，数据更新才允许
  const runner = effect(fn, { computed: true, lazy: true });
  return {
    effect: runner,
    get value() {
      //获取计算属性的值= xx.value 的原因
      return runner(); //此处返回方法
    },
  };
}
//传递依赖函数，
function effect(fn, options = {}) {
  //依赖函数，收集的动作
  //创建依赖对象
  let e = createReactiveEffect(fn, options);
  //通过lazy的computed配置
  if (!options.lazy) {
    //若不是懒执行
    e();
  }
  return e;
}

function createReactiveEffect(fn, options) {
  //构造固定格式的effect
  const effect = function effect(...args) {
    return run(effect, fn, args);
  };

  //effect的配置
  effect.deps = [];
  effect.computed = options.computed;
  effect.lazy = options.lazy;
  return effect;
}

function run(effect, fn, args) {
  //执行effect
  //取出effect,执行
  if (effectStack.indexOf(effect) === -1) {
    try {
      effectStack.push(effect);
      return fn(...args); //执行effect
    } finally {
      effectStack.pop(); //effect执行完毕
    }
  }
}

let effectStack = []; //存储effect
//定义全局targetMap
let targetMap = new WeakMap();

function track(target, key) {
  //收集依赖，追踪依赖
  const effect = effectStack[effectStack.length - 1];
  if (effect) {
    let depMap = targetMap.get(target);
    if (depMap === undefined) {
      depMap = new Map();
      targetMap.set(target, depMap);
    }
    let dep = depMap.get(key);
    if (dep === undefined) {
      dep = new Set(); //去重元素相同
      depMap.set(key, dep);
    }
    //容错,此处是核心代码
    if (!dep.has(effect)) {
      //新增依赖
      dep.add(effect);
      //双向存储 方便查找优化
      effect.deps.push(dep);
    }
  }
}
//如何收集依赖，用一个巨大的map对象来收集依赖
/**
 * {
 * target1:{
 * //key1:代表获取数据的对象，比如age,name
 *   key1:[依赖函数1，依赖函数2]
 * },
 * target2:{
 *   key2:[]
 * }
 * }
 */

function trigger(target, key, info) {
  //触发,数据变化后，通知更新,执行effect
  // 1.找到依赖
  const depMap = targetMap.get(target);
  if (depMap === undefined) {
    return;
  }
  // 分开，普通的effect,与computed有一个优先级，effects先执行，computed后执行
  const effects = new Set();
  //因为computed会可能依赖普通的effects
  const computedRunners = new Set();
  if (key) {
    let deps = depMap.get(key);
    deps.forEach((effect) => {
      if (effect.computed) {
        computedRunners.add(effect);
      } else {
        effects.add(effect);
      }
    });
    effects.forEach((effect) => effect());
    computedRunners.forEach((computed) => computed());
  }
}
